package scriptmanager

import (
	"bufio"
	"errors"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/wonderivan/logger"
	lua "github.com/yuin/gopher-lua"
	luapara "github.com/yuin/gopher-lua/parse"
)

var compiledLuaFile map[string]*lua.FunctionProto

//LuaManager lua脚本管理器。模块名必须与文件名相同
// 必须实现的控制接口：
// new				LUA函数形式：new(string param) (bool res,string err)
// Start			LUA函数形式：Start() (bool res,string err)
// Stop				LUA函数形式：Stop() (bool res,string err)
//
// 可选的事件接口：
// MQTTReciveEvent	LUA函数形式：MQTTReciveEvent(byte[] buffer) (bool res,string err)
// TCPReciveEvent	LUA函数形式：TCPReciveEvent(string key, byte[] buffer)
// TCPAcceptEvent	LUA函数形式：TCPAcceptEvent(string key)
// TimerElapsed		LUA函数形式：TimerElapsed()
//
// 可供调用的接口函数：
// MQTTSend			LUA函数形式：MQTTSend(byte[] buffer)(bool res,string error)
// TCPInit			LUA函数形式：TCPInit(string host,int port)(bool res,string error)
// TCPSend			LUA函数形式：TCPSend(string key,byte[] buffer)(bool res,string error)
// TCPClose			LUA函数形式：TCPClose()
// RTDBWrite		TODO:未实现
// RTDBRead			TODO:未实现
// HisDBWrite		TODO:未实现
// HisDBRead		TODO:未实现
// TimerInit		LUA函数形式：TimerInit(Int64 ms)(bool res,string error)
// TimerStart		LUA函数形式：TimerStart()
// TimerStop		LUA函数形式：TimerStop()
// TimerClose		LUA函数形式：TimerClose()
type LuaManager struct {
	ScriptBase
	filaPath      string
	luaContent    *lua.LTable
	luaRuntime    *lua.LState
	initLua       lua.LValue
	startLua      lua.LValue
	stopLua       lua.LValue
	mqttReciveLua lua.LValue
	tcpReciveLua  lua.LValue
	tcpAcceptLua  lua.LValue
	timerElapsed  lua.LValue
}

//Init 初始化脚本状态和变量，注入接口.
//LUA函数形式：Init(string param) (bool res,string err)
func (_this *LuaManager) Init(scriptName string, param string) (bool, error) {
	if compiledLuaFile == nil {
		compiledLuaFile = make(map[string]*lua.FunctionProto)
	}
	//编译lua文件
	if err := _this.compileLuaFile(scriptName); err != nil {
		return false, err
	}
	_, fileName := filepath.Split(scriptName)
	ext := path.Ext(scriptName)
	fileName = strings.TrimRight(fileName, ext)

	L := _this.luaRuntime
	_this.filaPath = scriptName

	paramLua := lua.LString(param)
	table := L.GetGlobal(fileName)
	if table.Type() != lua.LTTable {
		return false, errors.New("缺少结构体对象")
	}
	initFn := L.GetField(table, "new")
	if initFn.Type() != lua.LTFunction {
		return false, errors.New("缺少Init实现函数")
	}

	err := L.CallByParam(lua.P{
		Fn:      initFn,
		NRet:    3,
		Protect: true,
	}, table, paramLua)
	if err != nil {
		return false, err
	}
	resLua := L.Get(1)
	errLua := L.Get(2)
	meta := L.Get(3)
	L.Pop(3)
	if resLua != nil && resLua.Type() == lua.LTBool && !resLua.(lua.LBool) {
		if errLua != nil && errLua.Type() == lua.LTString {
			return false, errors.New(errLua.String())
		}
		return false, errors.New("初始化报错，无报错信息")
	}
	if meta == nil || meta.Type() != lua.LTTable {
		return false, errors.New("未能初始化实例")
	}
	_this.luaContent = meta.(*lua.LTable)
	if table.Type() != lua.LTTable {
		return false, errors.New("缺少结构体对象")
	}
	_this.injectLuaFunc()
	if res, err := _this.exportLuaFunc(); !res {
		_this.luaContent = nil
		return false, err
	}
	return true, nil
}

//exportLuaFunc 导出LUA函数到GO中
func (_this *LuaManager) exportLuaFunc() (bool, error) {
	var err error
	/*if _this.initLua, err = _this.getLuaFunction("Init"); err != nil {
		return false, err
	}*/
	if _this.startLua, err = _this.getLuaFunction("Start"); err != nil {
		return false, err
	}
	if _this.stopLua, err = _this.getLuaFunction("Stop"); err != nil {
		return false, err
	}
	if _this.mqttReciveLua, err = _this.getLuaFunction("MQTTReciveEvent"); err != nil {
		logger.Info(_this.filaPath + "MQTTReciveEvent 未加载")
	}
	if _this.tcpReciveLua, err = _this.getLuaFunction("TCPReciveEvent"); err != nil {
		logger.Info(_this.filaPath + "TCPReciveEvent 未加载")
		//return false, err
	} else {
		_this.TCPSetAcceptEvent(_this.tcpAcceptToLuaHandle)
	}
	if _this.tcpAcceptLua, err = _this.getLuaFunction("TCPAcceptEvent"); err != nil {
		logger.Info(_this.filaPath + "TCPAcceptEvent 未加载")
		//return false, err
	} else {
		_this.TCPSetReceivEvent(_this.tcpReceiveToLuaHandle)
	}
	if _this.timerElapsed, err = _this.getLuaFunction("TimerElapsed"); err != nil {
		logger.Info(_this.filaPath + "TimerElapsed 未加载")
		//return false, err
	} else {
		_this.TimerSetElapsed(_this.timeToLuaElapsedHandle)
	}
	return true, nil
}

//injectLuaFunc 注入go的函数接口给LUA
func (_this *LuaManager) injectLuaFunc() {
	//_this.luaRuntime.SetField(_this.luaContent, "Init", L.NewFunction(_this.rtdbWriteFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "MQTTSend", _this.luaRuntime.NewFunction(_this.mqttSendFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TCPInit", _this.luaRuntime.NewFunction(_this.tcpInitFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TCPSend", _this.luaRuntime.NewFunction(_this.tcpSendFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TCPClose", _this.luaRuntime.NewFunction(_this.tcpCloseFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "RTDBWrite", _this.luaRuntime.NewFunction(_this.rtdbWriteFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "RTDBRead", _this.luaRuntime.NewFunction(_this.rtdbReadFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "HisDBWrite", _this.luaRuntime.NewFunction(_this.hisdbWriteFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "HisDBRead", _this.luaRuntime.NewFunction(_this.hisdbReadFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TimerInit", _this.luaRuntime.NewFunction(_this.timerInitFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TimerStart", _this.luaRuntime.NewFunction(_this.timerStartFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TimerStop", _this.luaRuntime.NewFunction(_this.timerStopFromLua))
	_this.luaRuntime.SetField(_this.luaContent, "TimerClose", _this.luaRuntime.NewFunction(_this.timerCloseFromLua))
}
func (_this *LuaManager) getLuaFunction(funcName string) (lua.LValue, error) {
	//fn := _this.luaRuntime.GetGlobal(funcName)
	fn := _this.luaRuntime.GetField(_this.luaContent, funcName) //(funcName)
	if fn.Type() != lua.LTFunction {
		return nil, errors.New("缺少实现函数")
	}
	//fn.(*lua.LFunction).Env = _this.luaContent
	return fn, nil
}

//Start 启动脚本运行。
//LUA函数形式：Start() (bool res,string err)
func (_this *LuaManager) Start() (bool, error) {
	if _this.startLua == nil {
		return false, errors.New("未实现LUA函数：Start")
	}
	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.startLua,
		NRet:    2,
		Protect: true,
	}, _this.luaContent /*, lua.LString("aaaa")*/)
	if err != nil {
		return false, err
	}
	resLua := _this.luaRuntime.Get(1)
	errLua := _this.luaRuntime.Get(2)
	_this.luaRuntime.Pop(2)
	if errLua != nil && errLua.Type() != lua.LTNil {
		return false, errors.New("初始化错误")
	}
	if resLua != nil && resLua.Type() == lua.LTBool && !resLua.(lua.LBool) {
		if errLua != nil && errLua.Type() == lua.LTString {
			return false, errors.New(errLua.String())
		}
		return false, errors.New("初始化报错，无报错信息")
	}
	return true, nil
}

//Stop 停止脚本运行。
//LUA函数形式：Stop() (bool res,string err)
func (_this *LuaManager) Stop() (bool, error) {
	if _this.stopLua == nil {
		return false, errors.New("未实现LUA函数：Stop")
	}
	if compiledLuaFile != nil {
		delete(compiledLuaFile, _this.filaPath)
	}
	_this.luaRuntime.Close()

	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.stopLua,
		NRet:    2,
		Protect: true,
	}, _this.luaContent)
	if err != nil {
		return false, err
	}
	resLua := _this.luaRuntime.Get(1)
	errLua := _this.luaRuntime.Get(2)
	_this.luaRuntime.Pop(2)
	if errLua != nil && errLua.Type() != lua.LTNil {
		return false, errors.New("初始化错误" + errLua.String())
	}
	if resLua != nil && resLua.Type() == lua.LTBool && !resLua.(lua.LBool) {
		if errLua != nil && errLua.Type() == lua.LTString {
			return false, errors.New(errLua.String())
		}
		return false, errors.New("初始化报错，无报错信息")
	}
	return true, nil
}

//MQTTReciveToScript MQTT收到数据时此函数响应。此函数在接口中，会被父类直接调用.
//LUA函数形式：MQTTReciveEvent(byte[] buffer) (bool res,string err)
func (_this *LuaManager) MQTTReciveToScript(buffer []uint8) (bool, error) {
	if _this.mqttReciveLua == nil {
		return false, errors.New("未实现LUA函数：MQTTReciveEvent")
	}
	table := _this.getTableToLua(buffer)
	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.mqttReciveLua,
		NRet:    0,
		Protect: true,
	}, _this.luaContent, table)
	if err != nil {
		return false, err
	}
	return true, nil
}

//mqttSendFromLua MQTT发送数据时应调用此函数
//LUA函数形式：MQTTSend(byte[] buffer)(bool res,string error)
func (_this *LuaManager) mqttSendFromLua(L *lua.LState) int {
	buffer := _this.getBytesFromLua(L.ToTable(1))
	if res, err := _this.MQTTSendFromScript(buffer); !res {
		L.Push(lua.LString(err.Error()))
	} else {
		L.Push(lua.LNil)
	}
	return 1
}

//tcpInitFromLua TCP初始化时应调用此函数
//LUA函数形式：TCPInit(string host,int port)(bool res,string error)
func (_this *LuaManager) tcpInitFromLua(L *lua.LState) int {
	hostLua := L.Get(1)
	portLua := L.Get(2)
	if hostLua.Type() != lua.LTString || portLua.Type() != lua.LTNumber {
		L.Push(lua.LFalse)
		L.Push(lua.LString("函数参数不正确"))
		return 2
	}
	host := hostLua.(lua.LString) //L.ToString(1)
	port := portLua.(lua.LNumber) //L.ToInt(2)
	if float64(port) <= 0 || float64(port) > 65535 {
		L.Push(lua.LFalse)
		L.Push(lua.LString("使用端口不正确"))
		return 2
	}
	res := _this.TCPInit(host.String(), int(port))
	L.Push(lua.LBool(res))
	L.Push(lua.LNil)
	return 2
}

//tcpSendFromLua 发送TCP数据接口
//LUA函数形式：TCPSend(string key,byte[] buffer)(bool res,string error)
func (_this *LuaManager) tcpSendFromLua(L *lua.LState) int {
	keyLua := L.Get(1)
	bufferValue := L.Get(2)
	if keyLua.Type() != lua.LTString {
		L.Push(lua.LNumber(-1))
		L.Push(lua.LString("函数参数不正确"))
		return 2
	}
	key := keyLua.(lua.LString)
	buffer := _this.getBytesFromLua(bufferValue.(*lua.LTable))
	res, err := _this.TCPSend(string(key), buffer)
	L.Push(lua.LNumber(res))
	if err != nil {
		L.Push(lua.LString(err.Error()))
	} else {
		L.Push(lua.LNil)
	}
	return 2
}

//tcpCloseFromLua 关闭TCP连接接口
//LUA函数形式：TCPClose()
func (_this *LuaManager) tcpCloseFromLua(L *lua.LState) int {
	_this.TCPClose()
	return 0
}
func (_this *LuaManager) rtdbWriteFromLua(L *lua.LState) int {
	_this.RTDBWrite()
	return 0
}
func (_this *LuaManager) rtdbReadFromLua(L *lua.LState) int {
	_this.RTDBRead()
	return 0
}
func (_this *LuaManager) hisdbWriteFromLua(L *lua.LState) int {
	_this.HisDBWrite()
	return 0
}
func (_this *LuaManager) hisdbReadFromLua(L *lua.LState) int {
	_this.HisDBRead()
	return 0
}

//timerInitFromLua 初始化定时器
//LUA函数形式：TimerInit(Int64 ms)(bool res,string error)
func (_this *LuaManager) timerInitFromLua(L *lua.LState) int {
	msLua := L.Get(1)
	if msLua.Type() != lua.LTNumber {
		L.Push(lua.LFalse)
		L.Push(lua.LString("函数参数不正确"))
		return 2
	}
	ms := msLua.(lua.LNumber)
	if float64(ms) < 1 || float64(ms) > 9223372036854775807 {
		L.Push(lua.LFalse)
		L.Push(lua.LString("毫秒数值错误"))
	} else {
		_this.TimerInit(int64(ms))
		L.Push(lua.LTrue)
		L.Push(lua.LNil)
	}
	return 2
}

//timerStartFromLua 启动定时器
//LUA函数形式：TimerStart()
func (_this *LuaManager) timerStartFromLua(L *lua.LState) int {
	_this.TimerStart()
	return 0
}

//timerStopFromLua 暂停定时器
//LUA函数形式：TimerStop()
func (_this *LuaManager) timerStopFromLua(L *lua.LState) int {
	_this.TimerStop()
	return 0
}

//timerCloseFromLua 关闭定时器
//LUA函数形式：TimerClose()
func (_this *LuaManager) timerCloseFromLua(L *lua.LState) int {
	_this.TimerClose()
	return 0
}

//tcpReceiveToLuaHandle TCP收到数据事件
//LUA函数形式：TCPReciveEvent(string key, byte[] buffer)
func (_this *LuaManager) tcpReceiveToLuaHandle(key string, buffer []uint8) {
	table := _this.getTableToLua(buffer)
	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.tcpReciveLua,
		NRet:    0,
		Protect: true,
	}, _this.luaContent, lua.LString(key), table)
	if err != nil {
		logger.Error(err)
	}
}

//tcpAcceptToLuaHandle TCP接收到连接事件，给脚本一个属于连接的唯一标识Key
//LUA函数形式：TCPAcceptEvent(string key)
func (_this *LuaManager) tcpAcceptToLuaHandle(key string) {
	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.tcpAcceptLua,
		NRet:    0,
		Protect: true,
	}, _this.luaContent, lua.LString(key))
	if err != nil {
		logger.Error(err)
	}
}

//timeToLuaElapsedHandle 定时器定时事件
//LUA函数形式：TimerElapsed()
func (_this *LuaManager) timeToLuaElapsedHandle() {
	err := _this.luaRuntime.CallByParam(lua.P{
		Fn:      _this.timerElapsed,
		NRet:    0,
		Protect: true,
	}, _this.luaContent)
	if err != nil {
		logger.Error(err)
	}
}

func (_this *LuaManager) getBytesFromLua(table *lua.LTable) []uint8 {
	len := table.Len()
	buffer := make([]uint8, len, len)
	for i := 1; i <= len; i++ {
		res := table.RawGetInt(i)
		buffer[i-1] = uint8(res.(lua.LNumber))
	}
	return buffer
}

func (_this *LuaManager) getTableToLua(buffer []uint8) *lua.LTable {
	table := new(lua.LTable)
	for i := 0; i < len(buffer); i++ {
		table.Append(lua.LNumber(buffer[i]))
	}
	return table
}

func (_this *LuaManager) compileLuaFile(filePath string) error {
	if compiledLuaFile == nil {
		return errors.New("编译文件列表未初始化")
	}
	/*var stat *lua.FunctionProto
	var exist bool*/
	L := lua.NewState()
	if proto, exist := compiledLuaFile[filePath]; !exist {
		_, err := os.Lstat(filePath)
		if os.IsNotExist(err) {
			return err
		}
		file, err := os.Open(filePath)
		defer file.Close()
		if err != nil {
			return err
		}
		reader := bufio.NewReader(file)
		chunk, err := luapara.Parse(reader, filePath)
		if err != nil {
			return err
		}
		proto, err = lua.Compile(chunk, filePath)
		if err != nil {
			return err
		}
		if proto == nil {
			return errors.New("lua虚拟机编译存在问题")
		}
		compiledLuaFile[filePath] = proto
	}
	if L == nil {
		return errors.New("lua虚拟机未实例化")
	}
	lfunc := L.NewFunctionFromProto(compiledLuaFile[filePath])
	if lfunc == nil {
		return errors.New("lua虚拟机编译后实例化错误")
	}
	L.Push(lfunc)
	if err := L.PCall(0, lua.MultRet, nil); err != nil {
		return err
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		return err
	}
	dir = strings.Replace(dir, "\\", "\\\\", -1)
	dir = ";" + dir + "\\\\protocols\\\\scriptlibs\\\\lua\\\\?.lua"
	L.DoString("package.path = package.path..\"" + dir + "\"")

	_this.luaRuntime = L
	return nil
}
